﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DigitalQuran.Extensions
{
    /// <summary>
    /// Extension Methods for string
    /// </summary>
    public static class StringExtensions
    {
        //http://stackoverflow.com/questions/1368020/how-to-output-unicode-string-to-rtf-using-c
        /// <summary>
        /// Convert String to Unicode Escaped Charcters
        /// </summary>
        /// <param name="source">Source string</param>
        /// <returns>Returns Escaped Unicode String</returns>
        public static string ToUnicodeEscapedChars(this string source)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            StringBuilder str = new StringBuilder();
            foreach (char c in source)
            {
                if (c == '\\' || c == '{' || c == '}')
                    str.Append(@"\" + c);
                else if (c <= 0x7f)
                    str.Append(c);
                else
                    str.Append("\\u" + Convert.ToUInt32(c) + "?");
            }
            return str.ToString();
        }

        static Dictionary<char, string> rtfDictionary = new Dictionary<char, string>()
            {
                {'0', @"\'30" }, {'1', @"\'31" }, {'2', @"\'32" }, {'3', @"\'33" }, {'4', @"\'34" }, {'5', @"\'35" },
                {'6', @"\'36" }, {'7', @"\'37" }, {'8', @"\'38" }, {'9', @"\'39" }, {'ھ', @"\'AA" }, {'°', @"\'B0" },
                {'•', @"\'B7" }, {'ہ', @"\'C0" }, {'ء', @"\'C1" }, {'آ', @"\'C2" }, {'أ', @"\'C3" }, {'ؤ', @"\'C4" },
                {'إ', @"\'C5" }, {'ئ', @"\'C6" }, {'ا', @"\'C7" }, {'ب', @"\'C8" }, {'ة', @"\'C9" }, {'ت', @"\'CA" },
                {'ث', @"\'CB" }, {'ج', @"\'CC" }, {'ح', @"\'CD" }, {'خ', @"\'CE" }, {'د', @"\'CF" }, {'ذ', @"\'D0" },
                {'ر', @"\'D1" }, {'ز', @"\'D2" }, {'س', @"\'D3" }, {'ش', @"\'D4" }, {'ص', @"\'D5" }, {'ض', @"\'D6" },
                {'ط', @"\'D8" }, {'ظ', @"\'D9" }, {'ع', @"\'DA" }, {'غ', @"\'DB" }, {'ف', @"\'DD" }, {'ق', @"\'DE" },
                {'ك', @"\'DF" }, {'ل', @"\'E1" }, {'م', @"\'E3" }, {'ن', @"\'E4" }, {'ه', @"\'E5" }, {'و', @"\'E6" },
                {'ى', @"\'EC" }, {'ي', @"\'ED" }, {'ً', @"\'F0" }, {'ٌ', @"\'F1" }, {'ٍ', @"\'F2" }, {'َ', @"\'F3" },
                {'ُ', @"\'F5" }, {'ِ', @"\'F6" }, {'ّ', @"\'F8" }, {'ْ', @"\'FA" }, {'ے', @"\'FF" }
            };

        /// <summary>
        /// Convert Arabic Characters to RTF Charcters
        /// </summary>
        /// <param name="source">Source string</param>
        /// <returns>Return RTF Char string</returns>
        public static string ToRtfChars(this string source)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            var str = new StringBuilder();
            if (rtfDictionary != null)
            {
                foreach (char c in source)
                {
                    if (rtfDictionary.ContainsKey(c))
                        str.Append(rtfDictionary[c]);
                    else
                        str.Append(c);
                }
            }

            return str.ToString().ToLower();
        }

        /// <summary>
        /// Removes duplicates char form string
        /// </summary>
        /// <param name="source">Source string</param>
        /// <returns>Returns duplicate char free string</returns>
        public static string RemoveDuplicates(this string source)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            string result = "";
            foreach (char c in source)
            {
                if (!result.Contains(c))                
                    result += c;
            }

            return result;
        }

        /// <summary>
        /// Convert String to Camel Case String
        /// </summary>
        /// <param name="source">Source string</param>
        /// <returns>Returns camel case string</returns>
        public static string ToCamelCase(this string source)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            string result = "";
            source = source.ToLower();
            foreach (string words in source.Split(' '))
            {
                result += char.ToUpper(words[0]);
                result += (words.Substring(1, words.Length - 1) + ' ');
            }
            return result;
        }

        /// <summary>
        /// Check whether string is palindrome or not.
        /// </summary>
        /// <param name="source">Sorce string</param>
        /// <returns>Returns true if string is palindrome</returns>
        public static bool IsPalindrome(string source)
        {
            if (source == null) return false;
            if (source.Length == 0) return false;

            int fullLength = source.Length - 1;
            int halfLength = fullLength / 2;
            for (int i = 0; i <= halfLength; i++)
            {
                if (source.Substring(i, 1) != source.Substring(fullLength - i, 1))                
                    return false;                
            }

            return true;
        }

        /// <summary>
        /// Check wether string contains one or more Arabic chars
        /// </summary>
        /// <param name="source">Source string</param>
        /// <returns>Returns true if has arabic chars</returns>
        public static bool IsArabic(this string source)
        {
            if (source == null || source.Length == 0) return true;

            foreach (char c in source)
            {
                if (!Constants.ArabicLetters.Contains(c))                
                    return false;                
            }

            return true;
        }

        static readonly Dictionary<char, char> arabicDictionary = new Dictionary<char, char>()
            {
                { '\'', 'ء' }, { '>', 'أ' }, { '&', 'ؤ' }, { '<', 'إ' }, { '}', 'ئ' }, { 'A', 'ا' }, { 'b', 'ب' },
                { 'p', 'ة' }, { 't', 'ت' }, { 'v', 'ث' }, { 'j', 'ج' }, { 'H', 'ح' }, { 'x', 'خ' }, { 'd', 'د' },
                { '*', 'ذ' }, { 'r', 'ر' }, { 'z', 'ز' }, { 's', 'س' }, { '$', 'ش' }, { 'S', 'ص' }, { 'D', 'ض' },
                { 'T', 'ط' }, { 'Z', 'ظ' }, { 'E', 'ع' }, { 'g', 'غ' }, { '_', 'ـ' }, { 'f', 'ف' }, { 'q', 'ق' },
                { 'k', 'ك' }, { 'l', 'ل' }, { 'm', 'م' }, { 'n', 'ن' }, { 'h', 'ه' }, { 'w', 'و' }, { 'Y', 'ى' },
                { 'y', 'ي' }, { 'F', 'ً' }, { 'N', 'ٌ' }, { 'K', 'ٍ' }, { 'a', 'َ' }, { 'u', 'ُ' }, { 'i', 'ِ' },
                { '~', 'ّ' }, { 'o', 'ْ' }, { '^', 'ٓ' }, { '#', 'ٔ' }, { '`', 'ٰ' }, { '{', 'ٱ' }, { ':', 'ۜ' },
                { '@', '۟' }, { '"', '۠' }, { '[', 'ۢ' }, { ';', 'ۣ' }, { ',', 'ۥ' }, { '.', 'ۧ' }, { '!', 'ۨ' },
                { '-', '۪' }, { '+', '۫' }, { '%', '۬' }, { ']', 'ۭ' }
            };

        /// <summary>
        /// Letter-by-letter de-transliteration buckwater string to Arabic  
        /// </summary>
        /// <param name="source">Source string</param>
        /// <returns>Returns letter-by-letter detransliteration of buckwater string</returns>
        public static string ToArabic(this string source)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            StringBuilder str = new StringBuilder();
            foreach (char c in source)
            {
                if (arabicDictionary.ContainsKey(c))                
                    str.Append(arabicDictionary[c]);                
                else                
                    str.Append(c);                
            }
            
            return str.ToString();
        }

        static readonly Dictionary<char, char> buckwalterDictionary = new Dictionary<char, char>()
            {
                {'ء', '\'' }, { 'أ', '>' }, { 'ؤ', '&' }, { 'إ', '<' }, { 'ئ', '}' }, { 'ا', 'A' }, { 'ب', 'b' },
                { 'ة', 'p' }, { 'ت', 't' }, { 'ث', 'v' }, { 'ج', 'j' }, { 'ح', 'H' }, { 'خ', 'x' }, { 'د', 'd' },
                { 'ذ', '*' }, { 'ر', 'r' }, { 'ز', 'z' }, { 'س', 's' }, { 'ش', '$' }, { 'ص', 'S' }, { 'ض', 'D' },
                { 'ط', 'T' }, { 'ظ', 'Z' }, { 'ع', 'E' }, { 'غ', 'g' }, { 'ـ', '_' }, { 'ف', 'f' }, { 'ق', 'q' },
                { 'ك', 'k' }, { 'ل', 'l' }, { 'م', 'm' }, { 'ن', 'n' }, { 'ه', 'h' }, { 'و', 'w' }, { 'ى', 'Y' },
                { 'ي', 'y' }, { 'ً', 'F' }, { 'ٌ', 'N' }, { 'ٍ', 'K' }, { 'َ', 'a' }, { 'ُ', 'u' }, { 'ِ', 'i' },
                { 'ّ', '~' }, { 'ْ', 'o' }, { 'ٓ', '^' }, { 'ٔ', '#' }, { 'ٰ', '`' }, { 'ٱ', '{' }, { 'ۜ', ':' },
                { '۟', '@' }, { '۠', '"' }, { 'ۢ', '[' }, { 'ۣ', ';' }, { 'ۥ', ',' }, { 'ۧ', '.' }, { 'ۨ', '!' },
                { '۪', '-' }, { '۫', '+' }, { '۬', '%' }, { 'ۭ', ']' }
            };

        /// <summary>
        /// Letter-by-letter Buckwalter transliteration of the source arabic string
        /// </summary>
        /// <param name="source">Source String</param>
        /// <returns>Returns letter-by-letter transliteration of arabic string</returns>
        public static string ToBuckwalter(this string source)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            StringBuilder str = new StringBuilder();
            if (buckwalterDictionary != null)
            {
                foreach (char c in source)
                {
                    if (buckwalterDictionary.ContainsKey(c))
                    {
                        str.Append(buckwalterDictionary[c]);
                    }
                    else
                    {
                        str.Append(c);
                    }
                }
            }
            return str.ToString();
        }

        /// <summary>
        /// Check whether source string contain specified value using specific string comparison rule
        /// </summary>
        /// <param name="source">Source string</param>
        /// <param name="value">The string to seek.</param>
        /// <param name="comparisonType">One of the enumeration values that specifies the rules for the search.</param>
        /// <returns>returns true if source string contains specified value</returns>
        public static bool Contains(this string source, string value, StringComparison comparisonType)
        {
            if (source == null) return false;
            if (source.Length == 0) return false;

            return source.IndexOf(value, comparisonType) != -1;
        }

        /// <summary>
        /// Checks whether source string contains specified value
        /// </summary>
        /// <param name="source">Source string</param>
        /// <param name="value">Value to search</param>
        /// <returns>Returns true if found</returns>
        public static bool Contains(this string source, string value)
        {
            if (source == null) return false;
            if (source.Length == 0) return false;

            return Contains(source, value, false);
        }

        /// <summary>
        /// Checks whether source string contains specific value
        /// </summary>
        /// <param name="source">Source string</param>
        /// <param name="value">String value to search</param>
        /// <param name="caseSensitive">Tell whether to perform case sensitive search or not</param>
        /// <returns>Returns true if found</returns>
        public static bool Contains(this string source, string value, bool caseSensitive)
        {
            if (source == null) return false;
            if (source.Length == 0) return false;

            return caseSensitive ? source.Contains(value, StringComparison.InvariantCulture) : source.Contains(value, StringComparison.InvariantCultureIgnoreCase);
        }

        /// <summary>
        /// Returns specific chars form left of string
        /// </summary>
        /// <param name="source">Source string</param>
        /// <param name="count">Number of chars to return</param>
        /// <returns>Returns specified chars from left</returns>
        /// <exception cref="System.ArgumentException">Count argument out of range</exception>
        public static string Left(this string source, int count)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            if ((count >= 0) && (count < source.Length))
                return source.Substring(0, count);
            else
                throw new ArgumentException("Count is out of range.");            
        }

        /// <summary>
        /// Retive substring from this instance
        /// </summary>
        /// <param name="source">Source string</param>
        /// <param name="start">Start index</param>
        /// <param name="end">End index</param>
        /// <returns>Returns sub-string from start to end</returns>
        /// <exception cref="System.ArgumentException"></exception>
        public static string Mid(this string source, int start, int end)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            if (start > end &&
                start >= 0  && start < source.Length &&
                end >= 0    && end < source.Length)         
   
                return source.Substring(start, start + end);            
            else            
                throw new ArgumentException("Argument out of range");            
        }

        /// <summary>
        /// Retives sub-string from right
        /// </summary>
        /// <param name="source">Source string</param>
        /// <param name="count">No of chars to include in sub string</param>
        /// <returns>Returns sub-string of source string from right</returns>
        /// <exception cref="System.ArgumentException"></exception>
        public static string Right(this string source, int count)
        {
            if (source == null) return null;
            if (source.Length == 0) return null;

            if ((count >= 0) && (count < source.Length))
            {
                return source.Substring(source.Length - 1 - count);
            }
            else
            {
                throw new ArgumentException("Argument out of range");
            }
        }

        /// <summary> 
        /// returns true if source contains target in the middle (even if target is also found at the start and/or at the end), else false. 
        /// </summary>
        public static bool ContainsInside(this string source, string target)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source.Length <= target.Length) return false;

            // remove target from start (if any) repeatedly
            while (source.StartsWith(target))
            {
                source = source.Remove(0, target.Length);
            }

            // remove target from end (if any) repeatedly
            while (source.EndsWith(target))            
                source = source.Remove(source.Length - target.Length, target.Length);            

            return source.Contains(target);
        }

        /// <summary> 
        /// returns true if source contains any word of target, else false. 
        /// </summary>
        public static bool ContainsWordOf(this string source, string target)
        {
            return ContainsWordOf(source, target, false);
        }

        public static bool ContainsWordOf(this string source, string target, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            for (int j = 0; j < targetWordCount; j++)
            {
                for (int i = 0; i < sourceWordCount; i++)
                {
                    if (caseSensitive)
                    {
                        if (sourceWords[i].Contains(targetWords[j]))                        
                            return true;                        
                    }
                    else
                    {
                        if (sourceWords[i].ToLower().Contains(targetWords[j].ToLower()))
                            return true;                        
                    }
                }
            }
            return false;
        }

        /// <summary> 
        /// returns true if source contains word_count words of target in any order, else false. 
        /// </summary>
        public static bool ContainsWordsOf(this string source, string target, int word_count)
        {
            return ContainsWordsOf(source, target, word_count, false);
        }

        public static bool ContainsWordsOf(this string source, string target, int wordCount, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            int commonWordCount = 0;

            for (int j = 0; j < targetWordCount; j++)
            {
                for (int i = 0; i < sourceWordCount; i++)
                {
                    if (caseSensitive)
                    {
                        if (sourceWords[i].Contains(targetWords[j]))
                        {
                            sourceWords[i] = ""; // remove it from list so not to be reused
                            commonWordCount++;
                            break;
                        }
                    }
                    else
                    {
                        if (sourceWords[i].ToLower().Contains(targetWords[j].ToLower()))
                        {
                            sourceWords[i] = ""; // remove it from list so not to be reused
                            commonWordCount++;
                            break;
                        }
                    }
                }
            }
            if (commonWordCount >= wordCount)
                return true;
            
            return false;
        }

        /// <summary> 
        /// returns true if source contains all words of target in any order, else false. 
        /// </summary>
        public static bool ContainsWordsOf(this string source, string target)
        {
            return ContainsWordsOf(source, target, false);
        }

        public static bool ContainsWordsOf(this string source, string target, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] targetWords = target.Split(' ');
            int targetWordCount = targetWords.Length;

            return ContainsWordsOf(source, target, targetWordCount, caseSensitive);
        }

        /// <summary> 
        /// returns true if source contains any word of target_words, else false. 
        /// </summary>
        public static bool ContainsWordOf(this string source, List<string> targetWords)
        {
            return ContainsWordOf(source, targetWords, false);
        }

        public static bool ContainsWordOf(this string source, List<string> targetWords, bool caseSensitive)
        {
            if ((source == null) || (targetWords == null)) return false;
            if ((source.Length == 0) || (targetWords.Count == 0)) return false;

            string[] sourceWords = source.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Count;

            for (int j = 0; j < targetWordCount; j++)
            {
                for (int i = 0; i < sourceWordCount; i++)
                {
                    if (caseSensitive)
                    {
                        if (sourceWords[i].Contains(targetWords[j]))                        
                            return true;                        
                    }
                    else
                    {
                        if (sourceWords[i].ToLower().Contains(targetWords[j].ToLower()))
                            return true;                        
                    }
                }
            }
            return false;
        }

        /// <summary> 
        /// returns true if source contains word_count words of target_words in any order, else false. 
        /// </summary>
        public static bool ContainsWordsOf(this string source, List<string> targetWords, int wordCount)
        {
            return ContainsWordsOf(source, targetWords, wordCount, false);
        }

        public static bool ContainsWordsOf(this string source, List<string> targetWords, int wordCount, bool caseSensitive)
        {
            if ((source == null) || (targetWords == null)) return false;
            if ((source.Length == 0) || (targetWords.Count == 0)) return false;

            string[] sourceWords = source.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Count;

            int commonWordCount = 0;

            for (int j = 0; j < targetWordCount; j++)
            {
                for (int i = 0; i < sourceWordCount; i++)
                {
                    if (caseSensitive)
                    {
                        if (sourceWords[i].Contains(targetWords[j]))
                        {
                            sourceWords[i] = ""; // remove it from list so not to be reused
                            commonWordCount++;
                            break;
                        }
                    }
                    else
                    {
                        if (sourceWords[i].ToLower().Contains(targetWords[j].ToLower()))
                        {
                            sourceWords[i] = ""; // remove it from list so not to be reused
                            commonWordCount++;
                            break;
                        }
                    }
                }
            }

            return commonWordCount >= wordCount;
        }

        /// <summary> 
        /// returns true if source contains all words of target_words in any order, else false. 
        /// </summary>
        public static bool ContainsWordsOf(this string source, List<string> targetWords)
        {
            return ContainsWordsOf(source, targetWords, false);
        }

        public static bool ContainsWordsOf(this string source, List<string> targetWords, bool caseSensitive)
        {
            if ((source == null) || (targetWords == null)) return false;
            if ((source.Length == 0) || (targetWords.Count == 0)) return false;            

            return ContainsWordsOf(source, targetWords, targetWords.Count, caseSensitive);
        }

        /// <summary> 
        /// returns true if source has at least one similar word to any word of target, else false. 
        /// </summary>
        public static bool HasSimilarWordTo(this string source, string target, double similarityPercentage)
        {
            return HasSimilarWordTo(source, target, similarityPercentage, false);
        }

        public static bool HasSimilarWordTo(this string source, string target, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            for (int j = 0; j < targetWordCount; j++)
            {
                for (int i = 0; i < sourceWordCount; i++)
                {
                    if (sourceWords[i].IsSimilarTo(targetWords[j], similarityPercentage, caseSensitive))                    
                        return true;                    
                }
            }
            return false;
        }

        /// <summary> 
        /// returns true if source contains word_count words of target in any order, else false. 
        /// </summary>
        public static bool HasSimilarWordsTo(this string source, string target, int wordCount, double similarityPercentage)
        {
            return HasSimilarWordsTo(source, target, wordCount, similarityPercentage, false);
        }

        public static bool HasSimilarWordsTo(this string source, string target, int wordCount, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            int commonWordCount = 0;
            for (int j = 0; j < targetWordCount; j++)
            {
                for (int i = 0; i < sourceWordCount; i++)
                {
                    if (sourceWords[i].IsSimilarTo(targetWords[j], similarityPercentage, caseSensitive))
                    {
                        sourceWords[i] = ""; // remove it from list so not to be reused
                        commonWordCount++;
                        break;
                    }
                }
            }
            
            return commonWordCount >= wordCount;
        }

        /// <summary> 
        /// returns true if source and target have similar first word, else false. 
        /// </summary>
        public static bool HasSimilarFirstWordTo(this string source, string target, double similarityPercentage)
        {
            return HasSimilarFirstWordTo(source, target, similarityPercentage, false);
        }

        public static bool HasSimilarFirstWordTo(this string source, string target, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            if ((sourceWordCount > 0) && (targetWordCount > 0))
                return (sourceWords[0].IsSimilarTo(targetWords[0], similarityPercentage, caseSensitive));            
            else            
                return false;            
        }

        /// <summary> 
        /// returns true if source and target have similar last word, else false. 
        /// </summary>
        public static bool HasSimilarLastWordTo(this string source, string target, double similarityPercentage)
        {
            return HasSimilarLastWordTo(source, target, similarityPercentage, false);
        }

        public static bool HasSimilarLastWordTo(this string source, string target, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            if ((sourceWordCount > 0) && (targetWordCount > 0))            
                return (sourceWords[sourceWordCount - 1].IsSimilarTo(targetWords[targetWordCount - 1], similarityPercentage, caseSensitive));            
            else            
                return false;            
        }

        /// <summary> 
        /// returns true if source's first half has similar words to target in any order, else false. 
        /// </summary>
        public static bool HasSimilarFirstHalfTo(this string source, string target, double similarityPercentage)
        {
            return HasSimilarFirstHalfTo(source, target, similarityPercentage, false);
        }

        public static bool HasSimilarFirstHalfTo(this string source, string target, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            if ((sourceWordCount > 0) && (targetWordCount > 0))
            {
                int halfSourceWordCount = sourceWordCount / 2;
                int halfTargetWordCount = targetWordCount / 2;

                // adjust for odd counts
                halfSourceWordCount += (sourceWordCount % 2);
                halfTargetWordCount += (targetWordCount % 2);

                for (int i = 0; i < halfSourceWordCount; i++)
                {
                    bool similarWordFound = false;
                    for (int j = 0; j < halfTargetWordCount; j++)
                    {
                        if (j < halfSourceWordCount) // check for similarity in the same half length only
                        {
                            if (sourceWords[i].IsSimilarTo(targetWords[j], similarityPercentage))
                            {
                                similarWordFound = true;
                                break;
                            }
                        }
                    }

                    if (!similarWordFound)                    
                        return false;                    
                }

                // if source first half has similar words to target first half
                return true;
            }
            else            
                return false;            
        }

        /// <summary> 
        /// returns true if source's second half has similar words to target in order, else false. 
        /// </summary>
        public static bool HasSimilarSecondHalfTo(this string source, string target, double similarityPercentage)
        {
            return HasSimilarSecondHalfTo(source, target, similarityPercentage, false);
        }

        public static bool HasSimilarSecondHalfTo(this string source, string target, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;

            string[] sourceWords = source.Split(' ');
            string[] targetWords = target.Split(' ');
            int sourceWordCount = sourceWords.Length;
            int targetWordCount = targetWords.Length;

            if ((sourceWordCount > 0) && (targetWordCount > 0))
            {
                int halfSourceWordCount = sourceWordCount / 2;
                int halfTargetWordCount = targetWordCount / 2;

                // adjust for odd counts
                halfSourceWordCount += (sourceWordCount % 2);
                halfTargetWordCount += (targetWordCount % 2);

                for (int i = halfSourceWordCount; i < sourceWordCount; i++)
                {
                    bool similarWordFound = false;
                    for (int j = halfTargetWordCount; j < targetWordCount; j++)
                    {
                        if (j < sourceWordCount) // check for similarity in the same half length only
                        {
                            if (sourceWords[i].IsSimilarTo(targetWords[j], similarityPercentage))
                            {
                                similarWordFound = true;
                                break;
                            }
                        }
                    }

                    if (!similarWordFound)                    
                        return false;                    
                }

                // if source second half has similar words to target second half
                return true;
            }
            else            
                return false;            
        }

        /// <summary>
        /// Checks wether two string are similar are not upto specified percentage
        /// </summary>
        /// <param name="source">Source String to Compare With</param>
        /// <param name="target">Targeted String to Compare</param>
        /// <param name="similarityPercentage">Percentage Similarity betweeen strings</param>
        /// <returns>Returns true if both strings are similar</returns>
        public static bool IsSimilarTo(this string source, string target, double similarityPercentage)
        {
            return source.IsSimilarTo(target, similarityPercentage, false);
        }

        /// <summary>
        /// Checks wether two string are similar are not upto specified percentage
        /// </summary>
        /// <param name="source">Source String to Compare With</param>
        /// <param name="target">Targeted String to Compare</param>
        /// <param name="similarityPercentage">Percentage Similarity betweeen strings</param>
        /// <param name="caseSensitive">Tells wether to perform case sensitive comparison</param>
        /// <returns>Returns true if both strings are similar</returns>
        public static bool IsSimilarTo(this string source, string target, double similarityPercentage, bool caseSensitive)
        {
            if ((source == null) || (target == null)) return false;
            if ((source.Length == 0) || (target.Length == 0)) return false;
            if (source == target) return true;
            if (similarityPercentage == 1.0) return (source == target);

            double similarity = 1.0;
            double errorMargin = 0.001;

            if (source != target)            
                similarity = caseSensitive ? CalculateSimilarity(source, target) : CalculateSimilarity(source.ToLower(), target.ToLower());
            
            return (similarity >= (similarityPercentage - errorMargin));
        }

        /// <summary> 
        /// Calculate percentage similarity of two strings
        /// <param name="source">Source String to Compare with</param>
        /// <param name="target">Targeted String to Compare</param>
        /// <returns>Return Similarity between two strings from 0 to 1.0</returns>
        /// </summary>
        private static double CalculateSimilarity(string source, string target)
        {
            if ((source == null) || (target == null)) return 0.0;
            if ((source.Length == 0) || (target.Length == 0)) return 0.0;
            if (source == target) return 1.0;

            int stepsToSame = ComputeLevenshteinDistance(source, target);
            return (1.0 - ((double)stepsToSame / (double)Math.Max(source.Length, target.Length)));
        }

        /// <summary>
        /// returns the number of steps required to transform the source string into the target string. 
        /// Re: http://www.dotnetperls.com/levenshtein
        /// </summary>
        private static int ComputeLevenshteinDistance(string source, string target)
        {
            if ((source == null) || (target == null)) return 0;
            if ((source.Length == 0) || (target.Length == 0)) return 0;
            if (source == target) return source.Length;

            int sourceWordCount = source.Length;
            int targetWordCount = target.Length;

            int[,] distance = new int[sourceWordCount + 1, targetWordCount + 1];

            // Step 1
            if (sourceWordCount == 0)            
                return targetWordCount;
            
            if (targetWordCount == 0)            
                return sourceWordCount;            

            // Step 2
            for (int i = 0; i <= sourceWordCount; distance[i, 0] = i++) { }

            for (int j = 0; j <= targetWordCount; distance[0, j] = j++) { }

            // Step 3
            for (int i = 1; i <= sourceWordCount; i++)
            {
                //Step 4
                for (int j = 1; j <= targetWordCount; j++)
                {
                    // Step 5
                    int cost = (target[j - 1] == source[i - 1]) ? 0 : 1;

                    // Step 6
                    distance[i, j] = Math.Min(
                        Math.Min(distance[i - 1, j] + 1, distance[i, j - 1] + 1),
                        distance[i - 1, j - 1] + cost);
                }
            }

            // Step 7
            return distance[sourceWordCount, targetWordCount];
        }
    }
}